-module(test_aleph_table).
-include("aleph_data_interface.hrl").
-include_lib("eunit/include/eunit.hrl"). 
-include("aleph_naming_macros.hrl").
  
-export([dump_test/0,retreive_test/0,node_failed_test/0,just_seen_node_test/0,insert_node_test/0,find_node_test/0,split_bucket_test/0,invalidate_node_test/0,replace_node_test/0,initial_db_test/0,search_ets_test/0,locate_bucket_test/0]).

-import(aleph_config,[get_dump_db_interval/0]).
-import(aleph_utility,[foldl_with_break/3,while/2,int_pow/2]).
-import(aleph_dht_table,[initial_db/0,retreive/1,insert/3,insert_node/2,change_root/1,close/0,force_dump/0,find_node/1,replace_stale_node/2,
                         just_seen_node/1,invalidate_node/1,node_failed/1]).
-import(aleph_dht_table,[locate_bucket/1,split_bucket/1,replace_node/3,search_ets/1,insert_ets/1]).


%%%%%%%%%unit_test%%%%%%%%%%%%%%%%%%%%%%%
initial_db_test()->
  try
    mnesia:delete_table(?DHT_NODE_TAB),
    case ets:info(?DHT_BUCKET_TAB) of
        [{_,_}|_]->ets:db_delete(?DHT_BUCKET_TAB);
        undefined->continue
    end,
    {_,Ret}=initial_db(),    
    ?match(true,(Ret==ok)),
    Ret1=mnesia:table_info(?DHT_NODE_TAB,all),
    ?match(true,is_tuple(Ret1)==false),
    Ret2=ets:info(?DHT_BUCKET_TAB),   
    ?match(true,Ret2=/=undefined),
    Key=ets:first(?DHT_BUCKET_TAB),
    ?match(true,Key==int_pow(2,160))
  after
    aleph_dht_table:clear_buckets()
  end.
    
search_ets_test()->
  try
    initial_db(),
    insert_ets(dht_bucket:create(int_pow(2,161),int_pow(2,160),[],now())),    
    Bucket=search_ets(int_pow(2,161)),   
    ?match(true,int_pow(2,161)==Bucket(max))
  after
    aleph_dht_table:clear_buckets()
  end. 

locate_bucket_test()->    
  try
    initial_db(),
    insert_ets(dht_bucket:create(int_pow(2,161),int_pow(2,160),[],now())),
    Bucket=locate_bucket(1),    
    ?match(true,int_pow(2,160)==Bucket(max)),
    Bucket1=locate_bucket(int_pow(2,160)+100),    
    ?match(true,int_pow(2,161)==Bucket1(max)),
    Bucket2=locate_bucket(int_pow(2,161)+100),    
    ?match(true,[]==Bucket2)
  after
    aleph_dht_table:clear_buckets()
  end.

build_nodes3()->
    lists:reverse(lists:foldl(fun(Y,ACC)->              
              [?NODE(dht_node:create(Y,2009,9009,Y,0,100))|ACC] end,
              [],lists:seq(1,10))).
build_nodes1()->  
    Bucket=search_ets(int_pow(2,160)),  
    Nodes=build_nodes3(), 
    NewBucket=Bucket({nodes,Nodes}),    
    insert_ets(NewBucket).

-define(CREATE_NODES(Y),[
?NODE(dht_node:create(Y-1,2009,9009,Y-1,0,100)),
?NODE(dht_node:create(Y-2,2009,9009,Y-2,0,100)),
?NODE(dht_node:create(Y-3,2009,9009,Y-3,0,100))]).

build_nodes2()->  
    build_nodes1(),
    split(),
    Key=16,        
    lists:foldl(fun(_,ACC)->
           Key2=ets:next(?DHT_BUCKET_TAB,ACC),            
           Bucket=search_ets(Key2),           
           insert_ets(Bucket({nodes,?CREATE_NODES(Key2)})),               
           Key2              
           end,Key,lists:seq(0,8)),
    ok.

split()->
     while(fun()->
       Bucket=locate_bucket(1),
       Len=length(Bucket(nodes)),
       %io:format("nodes lenght~p~n",[Len]),
       case Len>=?BUCKET_SIZE of
            true->split_bucket(Bucket),
               %io:format("split bucket~n"), 
                [];
            false->                
               %io:format("split bucket return~n"),
                {return,ok}
        end end,[]).

replace_node_test()->
   try
     initial_db(), 
     build_nodes1(),
     Stale=dht_node:create(7,2009,9009,7,0,100),
     New=dht_node:create(7,3009,19009,7,0,100),
     replace_stale_node(Stale,New),
     Bucket=locate_bucket(7),
     {_,{_,Node}}=lists:keysearch(7,1,Bucket(nodes)),
     {_,Node2}=lists:nth(7,Bucket(nodes)),
     ?match(true,3009==Node(port)), 
     ?match(true,Node2=:=Node)
   after
    aleph_dht_table:clear_buckets()
   end.      

invalidate_node_test()->
  try
    initial_db(), 
    build_nodes1(),    
    Stale=dht_node:create(7,2009,9009,7,0,100),
    invalidate_node(Stale),
    Bucket=locate_bucket(Stale(num)),
    Ret=lists:keysearch(7,1,Bucket(nodes)),
        io:format("5 ~p~n",[Ret]),
    ?match(true,Ret==false)
  after
    aleph_dht_table:clear_buckets()
  end.

split_bucket_test()->
  try
    initial_db(), 
    build_nodes1(),
    split(),
    {_,{_,Size}}=lists:keysearch(size,1,ets:info(?DHT_BUCKET_TAB)),
    %io:format("Size ~p ~n",[Size]),
    ?match(true,Size==160-3+1),
    Key=ets:first(?DHT_BUCKET_TAB),
    %io:format("Key ~p ~n",[Key]),
    Bucket=search_ets(Key),
    Key2=ets:next(?DHT_BUCKET_TAB,Key),
    %io:format("Key2 ~p~n",[Key2]),
    Bucket2=search_ets(ets:next(?DHT_BUCKET_TAB,Key)),
    %io:format("Bucket ~p~n",[Bucket]),
    %io:format("Bucket2 ~p~n",[Bucket2]),
    %io:format("Bucket.nodes ~p~n",[Bucket(nodes)]),
    %io:format("Bucket2.nodes ~p~n",[Bucket2(nodes)]),
    lists:foldl(fun(X,[{Id,_}|T])->?match(true,X==Id),T end,
                Bucket(nodes),lists:seq(1,7)),
    lists:foldl(fun(X,[{Id,_}|T])->?match(true,X==Id),T end,
                Bucket2(nodes),lists:seq(8,10)),
    ok
  after
    aleph_dht_table:clear_buckets()
  end.

find_node_test()->
   try
    initial_db(), 
    build_nodes2(), 
    [H|_]=find_node(2),
    ?match(true,2==H(num)),
    List=find_node(512-5),
    ?match(true,?BUCKET_SIZE==length(List)),
    while(fun([_|[]])->{return,ok};
       ([H1,H2|T])->
       case (H1(num) bxor 507) <(H2(num) bxor 507) of
           true->[[H2|T]];
           false->?match(true,false)
       end end,[List])    
   after
    aleph_dht_table:clear_buckets()
   end.

insert_node_test()->
     try
       %prepare data
       Nodes=[X||{_,X}<-build_nodes3()],       
       Root=dht_node:create(1,2009,9009,1,0,100),
       change_root(Root),
       lists:foreach(fun(X)->
           insert_node(X,true) end, lists:sublist(Nodes,8)),
       {_,{_,Size}}=lists:keysearch(size,1,ets:info(?DHT_BUCKET_TAB)),
       io:format("Size ~p ~n",[Size]),
       ?match(true,Size==1),    
       %test insert with root2 node and return old node 
       Root2=dht_node:create(700,2009,9009,700,0,100),
       TestNode=dht_node:create(9,2009,9009,9,0,100),
       change_root(Root2),
       {ok,Old}=insert_node(TestNode,true),
       Bucket=locate_bucket(1),
       [{_,Head}|_]=Bucket(nodes),
       io:format("H~p,Old~p~n",[Head,Old]),
       ?match(true,Head(self)=:=Old(self)),       
       %test insert with root1 node
       change_root(Root),
       lists:foreach(fun(X)->
           insert_node(X,true) end,lists:sublist(Nodes,9,10)),
       {_,{_,Size2}}=lists:keysearch(size,1,ets:info(?DHT_BUCKET_TAB)),
       io:format("Size2 ~p ~n",[Size2]),
       ?match(true,Size2==160-3+1),           
       Bucket1=locate_bucket(8),       
       {_,{_,Ret}}=lists:keysearch(9,1,Bucket1(nodes)),       
       ?match(true,9==Ret(num)),    
       %test insert with updating lastseen              
       Temp=find_node(3),
       io:format("X Temp~p~n",[Temp]),
       [H|_]=Temp,
       %io:format("X H~p~n",[H]),
       Bucket3=locate_bucket(3),
       LastAccessed=Bucket3(lastAccessed),
       LastSeen=H(lastseen),
       insert_node(H,true),
       Bucket4=locate_bucket(3),
       {_,LastElement}=lists:last(Bucket4(nodes)),
       {_,ThirdElement}=lists:nth(3,Bucket4(nodes)),
       ?match(false,LastAccessed=:=Bucket4(lastAccessed)),
       ?match(true,H(num)==LastElement(num)),
       ?match(false,LastSeen==LastElement(lastseen)),
       ?match(false,ThirdElement(num)==LastElement(num))
     after
      aleph_dht_table:clear_buckets()
     end.

just_seen_node_test()->
      try
        initial_db(), 
        build_nodes1(),
        [H|_]=find_node(1),               
        Tsamp=just_seen_node(1),                   
        ?match(true,Tsamp==H(lastseen)),        
        [H1|_]=find_node(1), 
        ?match(true,H1(lastseen)>H(lastseen))
      after
        aleph_dht_table:clear_buckets()
      end.  

node_failed_test()->
    try
        initial_db(), 
        build_nodes1(),        
        lists:foreach(fun(_)->
            [H|_]=find_node(1),          
            node_failed(H) end
            ,lists:seq(1,?MAX_FAILURES+1)),
        List=find_node(1),        
        ?match(true,1=/=length(List))
     after
        aleph_dht_table:clear_buckets()
     end.         
        
dump_test()->
     try   
        mnesia:delete_table(?DHT_NODE_TAB),
        initial_db(),         
        Size1=mnesia:table_info(?DHT_NODE_TAB,size),  
        %io:format("Size ~p  ~n",[Size1]),     
        ?match(true,Size1==0),
        build_nodes1(), 
        split(),
        force_dump(),
        Size=mnesia:table_info(?DHT_NODE_TAB,size),
        %io:format("Size ~p ~n",[Size]),
        ?match(true,Size==10)    
     after
        aleph_dht_table:clear_buckets()
     end.

retreive_test()->
     try   
        initial_db(), 
        %io:format("initial_db~n"), 
        Root=dht_node:create(200,2009,9009,11,0,100),
        change_root(Root),
        build_nodes1(), 
        %io:format("build_nodes1~n"), 
        split(),
        %io:format("split~n"),
        force_dump(),
        %io:format("force_dump~n"),
        retreive(Root),
        {_,{_,Size}}=lists:keysearch(size,1,ets:info(?DHT_BUCKET_TAB)),
        %io:format("Size ~p ~n",[Size]),
        %Key1=ets:first(?DHT_BUCKET_TAB),
        %Bucket1=search_ets(Key1),
        %io:format("First ~p ~n",[Bucket1(nodes)]),         
        ?match(true,Size==160-3+1)         
     catch
      exit:Reason->io:format("Exit Reason ~p~n",[Reason]),?match(true,false)
     after
       aleph_dht_table:clear_buckets()
     end.
    

    
